#include "sockets.h"
#include "../../Mensajes.h"

// Semáforo del thread socket
extern sem_t mutex_socket;

// Semáforo del thread socket
extern sem_t mutex_socket_s;
extern sem_t socket_disp;
extern sem_t cliente_conectado;

// Semáforos movimientos
extern sem_t nuevo_mov;
extern sem_t sem_mov;
extern sem_t mov_valido;

// Mensaje inicio
extern Mensaje msj_inicio;

// Flag estado socket servidor (RECV,SEND,INICIO,CONF)
int flag=0;

// Descriptor a operar
extern int socket_op;

void configurar_cliente(cl_config* arg)
{
	FILE* arch;
	
	if((arch = fopen("config/client_cfg","rt")) == NULL)
	{
		printf("No se puede leer configuración\n");
		exit(1);
	}
	
	fscanf(arch,"IP = %s\nPUERTO = %d",arg->ip_srv,&(arg->puerto));
}

void configurar_servidor(sv_config* arg)
{
	FILE* arch;
	
	if((arch = fopen("config/server_cfg","rt")) == NULL)
	{
		printf("No se puede leer configuración\n");
		exit(1);
	}
	
	fscanf(arch,"PUERTO = %d\nPARTIDAS = %d",&(arg->puerto),&(arg->cant_partidas));
}

void conectar_a_socket(int* sock_server,cl_config* configuracion)
{
	// Funcion que inicializa el socket
	struct hostent *he;
	struct sockaddr_in server;
	
	if((he = gethostbyname(configuracion->ip_srv)) == NULL)
	{
		printf("Error de host: %s\n",strerror(errno));
		exit(1);
	}

	// Crear socket
	if((*sock_server = socket(AF_INET, SOCK_STREAM, 0))<0)
	{
		printf("Error en socket: %s\n",strerror(errno));
		exit(1);
	}
	
	// Definir propiedades del socket
	server.sin_family = AF_INET;
	server.sin_port = htons(configuracion->puerto);
	server.sin_addr.s_addr = ((struct in_addr *) (he->h_addr))->s_addr;
	bzero(&(server.sin_zero),8);

	printf("Conectando a %s:%d...\n",configuracion->ip_srv,configuracion->puerto);
	
	// Conectar
	if(connect(*sock_server, (struct sockaddr*) &server, sizeof(struct sockaddr_in)) == -1)
	{
		printf("Error en connect: %s\n",strerror(errno));
		close(*sock_server);
		exit(1);
	}	
	
    sem_post(&cliente_conectado);
	// Conectado
	printf("Se establecio la conexion con el servidor.\n");
}

void iniciar_socket(int* sock,sv_config* configuracion) 
{
	// Funcion que inicializa el socket del servidor por unica vez	
	int val=1;
	struct sockaddr_in srv;
	
	// Borrar
	struct sockaddr_in socketdir_cliente1,socketdir_cliente2; 
	int addrlen = sizeof( (struct sockaddr *) &socketdir_cliente1);
	
	// Crear socket
	if((*sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("Error en socket: %s\n",strerror(errno));
		exit(1);
	}
	
	// Definir propiedades del socket
	srv.sin_family = AF_INET;
	srv.sin_port = htons(configuracion->puerto);
	srv.sin_addr.s_addr = inet_addr(HOST);	
	//inet_aton("127.0.0.1", &srv.sin_addr.s_addr);

	// Reuse
	if(setsockopt(*sock,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(int)) == -1)
	{
		printf("Error en socket: %s\n",strerror(errno));
		exit(1);
	}
	
	// Vincular socket
	if(bind(*sock,(struct sockaddr*)&srv,sizeof(struct sockaddr)) <0)
	{
		printf("Error en bind: %s\n",strerror(errno));
		close(*sock);
		exit(1);
	}

	// Escuchar
	if(listen(*sock,2) <0)
	{
		printf("Error en listen: %s\n",strerror(errno));
		close(*sock);
		exit(1);
	}
}

// Thread socket en cliente
void* thread_socket_c(void* arg)
{
	int socket,accion = -2;
	cl_config config;
	s_mov* mov = (s_mov*) arg;
	int aux=-2;
		
	// Cargar configuración
	configurar_cliente(&config);
	
	printf("IP: %s | Puerto: %d\n",config.ip_srv,config.puerto);
	
	// Iniciar comunicación
	conectar_a_socket(&socket,&config);
	
	// Socket listo para uso
	sem_post(&socket_disp);
	
	while(1)
	{
		// Espero desbloqueo
		lock_socket();
		
		sem_getvalue(&sem_mov,&aux);
		printf("    <<< sem_mov: %d |",aux);
		sem_getvalue(&nuevo_mov,&aux);
		printf("nuevo_mov: %d |",aux);
		sem_getvalue(&socket_disp,&aux);
		printf("socket_disp: %d\n",aux);
		
		switch(flag)
		{
			case INICIO:
				// Envío nombre de jugador
				printf("    <<< Socket envia señal inicio...");
				if(send(socket,&msj_inicio,sizeof(Mensaje),0) == -1)
				{
					perror("sedirnd");
					exit(1);
				}
				
			case NUEVO_CONTRINCANTE:
				// Señal inicio partida
				printf(" Socket espera señal inicio\n");
				if(recv(socket,&msj_inicio,sizeof(Mensaje),0) <= 0)
				{
					printf("recv");
					exit(1);
				}
				printf("    <<< Sincro hecha\n");
				
				// Hay nuevo movimiento
				sem_post(&socket_disp);
				sem_post(&nuevo_mov);
				break;
		
			case RECV:
				// Mutex movimiento
				sem_wait(&sem_mov);
				
				do
				{
					// Espera señal de turno
					printf("    <<< Socket espera señal... ");
					if(recv(socket,mov,sizeof(s_mov),0) <= 0)
					{
						printf("recv");
						exit(1);
					}
					printf("Recv: %d|%d|%d|%d\n",mov->accion,mov->estado,mov->posicion,mov->n_posicion);
				}while(mov->estado<-2 || mov->estado>3 || mov->n_posicion<0 || mov->posicion>9);
			
				// Hay nuevo movimiento
				sem_post(&nuevo_mov);
				sem_post(&sem_mov);
				
				// Thread libre
				sem_post(&socket_disp);
				break;
			
			case SEND:
				// Mutex movimiento
				sem_wait(&sem_mov);
				
				// Envío movimiento al servidor
				if(send(socket,mov,sizeof(s_mov),0) == -1)
				{
					printf("sedirnd");
					exit(1);
				}
				
				printf("    <<< Socket envió pos: %d\n",mov->posicion);
				
				// Espera confirmación
				if(recv(socket,mov,sizeof(s_mov),0) <= 0)
				{
					printf("recv");
					exit(1);
				}
				
				printf("    <<< Socket recibió validacion: %d\n",mov->accion);
						
				// Hay nuevo movimiento
				sem_post(&nuevo_mov);
				sem_post(&sem_mov);
				
				// Thread libre
				sem_post(&socket_disp);
				
				printf("    <<< Ciclo socket\n");
				break;
		}
	}
}

// Thread socket en servidor
void* thread_socket_s(void* arg)
{
	int value=-1;
	
	sv_config config;
	s_mov* mov = (s_mov*) arg;
	s_mov buffer;
	
	// Cargar configuración
	configurar_servidor(&config);
	
	printf("Puerto: %d\n",config.puerto);

	// Thread libre
	sem_post(&socket_disp);
	
	while(1)
	{
		// Espero desbloqueo
		lock_socket_s();
		
		if(flag == INICIO)									// Envío mensaje inicio a clientes
		{
			printf("    <<< Inicio para jug: %d... ",socket_op);
				
			if(send(socket_op,&msj_inicio,sizeof(Mensaje),0) == -1)
				exit(1);
						
			printf("Inicio enviado\n");
			flag=socket_op=-1;
		
			// Thread libre
			sem_post(&socket_disp);
			sem_post(&nuevo_mov);
		}
		
		else
		{
			if(flag == CONF)									// Confirmación movimientos
			{
				printf("    <<< Confirmando para jug: %d... ",socket_op);
				
				// Copio movimiento
				buffer.accion = mov->accion;
				buffer.estado = mov->estado;
				buffer.posicion = mov->posicion;
				buffer.n_posicion = mov->n_posicion;
				
				// Mutex movimiento
				sem_post(&sem_mov);
				
				if(send(socket_op,&buffer,sizeof(s_mov),0) == -1)
					exit(1);
							
				printf("Confirmado (%d,%d,%d,%d)\n",buffer.accion,buffer.estado,buffer.posicion,buffer.n_posicion);
				flag=socket_op=-1;
			
				// Thread libre
				sem_post(&socket_disp);
			}
			
			else 															// Envío y recepción
			{
				printf("    <<< Envio/recepcion jug: %d... ",socket_op);
				
				// Mutex movimiento
				sem_wait(&sem_mov);
				
				if(send(socket_op,mov,sizeof(s_mov),0) == -1)
					exit(1);
				printf("Envio (%d,%d,%d,%d)\n",mov->accion,mov->estado,mov->posicion,mov->n_posicion);
				if(recv(socket_op,mov,sizeof(s_mov),0) <= 0)
					exit(1);
				
				printf("    <<< Recibo posicion %d\n",mov->posicion);
				
				// A modo de confirmación
				sem_post(&nuevo_mov);	
				
				flag=socket_op=-1;
				
				// Thread libre
				sem_post(&socket_disp);
				
				// Mutex movimiento
				sem_post(&sem_mov);
			}
		}
	}
}

// Bloquear socket server
void lock_socket_s()
{
	sem_wait(&mutex_socket_s);
}

// Desbloquear socket server, recibe flag y descriptor
void unlock_socket_s(int est, int sock_jug)
{
	// Thread ocupado
	sem_wait(&socket_disp);
	
	flag = est;
	socket_op = sock_jug;
	sem_post(&mutex_socket_s);
	printf("    <<< Socket_s unlocked, flag: %d\n",flag);
}

// Bloquear socket cliente
void lock_socket()
{
	sem_wait(&mutex_socket);
}

// Desbloquear socket cliente, recibe flag
void unlock_socket(int est)
{
	// Thread ocupado
	sem_wait(&socket_disp);
	
	flag = est;
	sem_post(&mutex_socket);
	printf("    <<< Socket unlocked, flag: %d\n",flag);
}
